import { reactive, Ref, ref, unref, watch } from "vue";
import { omit, pick } from "lodash-es";
import { useRouter } from "vue-router";
import { useUrlSearchParams } from "@vueuse/core";

import useMyFetch from "@/utils/fetch";
import { isNilStrong } from "@/utils/tool";

interface TPageData<TData = any> {
  page: number;
  size: number;
  total: number;
  list: TData[];
}

type ToRef<T> = T | Ref<T>;

export interface UsePageOptions<TSearch extends object, TData> {
  page?: TPageData<TData>["page"];
  size?: TPageData<TData>["size"];
  defaultSearch?: ToRef<Partial<TSearch>>;

  method?: "get" | "post";
  immediate?: boolean;
  initialData?: {
    list: TData[];
  };

  foramtFetchParams?: (
    e: TSearch & Partial<Pick<TPageData, "page" | "size">>
  ) => any;

  formatUrlParams?: (item: any) => object | null;

  /**
   * 请求参数展示在url上
   */
  routerQuery?: boolean;
}

const usePage = <TData, TSearch extends object = {}>(
  url: string | Ref<string>,
  options: UsePageOptions<TSearch, TData> = {}
) => {
  const router = useRouter();
  // 获取浏览器URL数据和操作
  const urlParams = useUrlSearchParams("history");

  const {
    page = 1,
    size = 20,
    initialData,
    routerQuery,
    method = "post",
    immediate = !!url,
    defaultSearch = {},

    foramtFetchParams = (e) => e,
    formatUrlParams = (e) => e,
  } = options;

  const pagination = reactive({
    page: (routerQuery ? +urlParams.page : 0) || page,
    size: (routerQuery ? +urlParams.size : 0) || size,
    total: 0,
  });

  const params = ref<ToRef<Partial<TSearch>>>(defaultSearch);
  const list = ref<TData[]>(initialData?.list || []);

  // 开启routerQuery时，设置URL得searchQuery值
  if (routerQuery) {
    Object.assign(
      params.value,
      formatUrlParams(omit(urlParams, ["page", "size"]))
    );
  }

  const getURL = () => {
    const searchParams = {
      page: pagination.page,
      size: pagination.size,
      ...(unref(params) as object),
    } as object;

    // @ts-expect-error
    return `${unref(url)}?${new URLSearchParams(searchParams).toString()}`;
  };

  const instance = useMyFetch<TPageData<TData>>(
    method == "get" ? getURL : url,
    { immediate, initialData }
  ).json();

  const fetchParams = () => {
    const originParams = {
      ...(unref(params) as TSearch),
      ...pick(pagination, ["page", "size"]),
    };

    const searchParams = foramtFetchParams(originParams);

    if (routerQuery) {
      setUrlParams(originParams);
    }

    return searchParams;
  };

  /**
   * 设置参数到URL query
   * @param params
   */
  const setUrlParams = (params: any) => {
    Promise.resolve().then(() => {
      for (let [key, val] of Object.entries(params)) {
        Reflect.set(urlParams, key, isNilStrong(val) ? undefined : val);
      }

      router.push({ query: urlParams });
    });
  };

  if (method != "get") {
    instance[method](fetchParams);
  }

  const {
    isFetching,
    error,
    data,
    abort,
    canAbort,
    onFetchResponse,
    onFetchError,
    execute,
  } = instance;

  onFetchResponse(() => {
    pagination.total = data.value?.total || 0;
    list.value = data.value?.list || [];
  });

  // watch([() => pagination.page, () => pagination.size], ([page, size]) => {
  //   urlParams.page = page + "";
  //   urlParams.page = size + "";
  //   execute();
  // });

  function onPageChange(page: number) {
    pagination.page = page;
    execute();
  }

  function onPageSizeChange(pageSize: number) {
    pagination.size = pageSize;
    execute();
  }

  return {
    pagination,

    isFetching,
    error,
    data,
    list,
    canAbort,

    abort,
    execute,
    onFetchResponse,
    onFetchError,
    onPageChange,
    onPageSizeChange,
  };
};

export default usePage;
